#include<fcntl.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<errno.h>
#include<sys/socket.h>
#include<string.h>
#include<stdlib.h>
#include<sys/epoll.h>
#include<unistd.h>
#include <netinet/in.h> 

struct sockitem
{
	int fd;
	int (*callback)(int fd,int events,void*args);

	char recvbuffer[1024];
	char sendbuffer[1024];
};

struct reactor
{
	int epfd;
	struct epoll_event events[512];
};


struct reactor* eventloop=NULL;

int recv_cb(int fd,int events,void*args);
int send_cb(int fd,int events,void*args);
int accept_cb(int fd,int events,void*args)
{
		printf("建立连接成功\n");

		struct sockaddr_in client_addr;
		memset(&client_addr,0,sizeof(struct sockaddr_in));
		socklen_t len=sizeof(client_addr); 
		int clientfd=accept(fd,(struct sockaddr*)&client_addr,&len);

		if(clientfd<=0)
		{
			printf("建立连接失败");
			return -1;

		}

		char str[INET_ADDRSTRLEN]={0};

		
		printf("客户端地址为%s,端口号为:%d\n",inet_ntop(AF_INET,&client_addr.sin_addr,str,sizeof(str)),
		ntohs(client_addr.sin_port));

		struct epoll_event ev;
		ev.events=EPOLLIN|EPOLLET;
		
		struct sockitem* si=(struct sockitem*)malloc(sizeof(struct sockitem));
		si->fd=clientfd;
		si->callback=recv_cb;
		ev.data.ptr=si;
		epoll_ctl(eventloop->epfd,EPOLL_CTL_ADD,clientfd,&ev);
		return clientfd;
}


int recv_cb(int fd,int events,void*args)
{
		char buffer[1024]={0};
		struct sockitem* si=(struct sockitem*)args;
		struct epoll_event ev;
		//非阻塞情况下recv如果没有数据可读会返回-1,其他情况比如网络异常会将errno设置为相应的值
		int n=recv(fd,buffer,1024,0);

		if(n<0)
		{
			if(errno==EAGAIN||errno==EWOULDBLOCK)
			{
			printf("没有数据可读");
			return -1;
			}else
			{
			return -1;
			}

			ev.events=EPOLLIN;

			epoll_ctl(eventloop->epfd,EPOLL_CTL_DEL,fd,&ev);

			close(fd);

			free(si);
			
		}
		else if(n==0)
		{
		//客户端断开与服务器端的连接,recv会返回0
		//客户端向服务器端发送FIN包,然后服务器端回了确认消息，如果服务器端不向客户端主动发送FIN包，那么服务器端就会存在
		//time_wait状态等待一段时间，因为服务器可能要处理一些其他的事务，处理完之后，服务器端向客户端发送FIN包，也就是服务
		//器端主动close(clienfd);之后客户端收到了服务端的FIN包，并确认后，连接才正式关闭
		//这里可能会处理一些其他的业务逻辑，处理业务逻辑所需的时间也就是time_wait的时间

			printf("断开连接...\n");
			
			ev.events=EPOLLIN;
			epoll_ctl(eventloop->epfd,EPOLL_CTL_DEL,fd,&ev);

			close(fd);

			free(si);	
			
		}
		else
		{
			printf("recv=%s,收到的字节数为=%d\n",buffer,n);

			//服务器端在收到客户端的数据后，如果服务器端向客户端在发送数据，
			//会先将数据发送到发送缓冲区，如果发送缓冲区满了，那么就不能在发送了
			/*if(send()==-1)
			{
				//说明发送缓冲区满了
			}*/
			struct epoll_event ev;
			ev.events=EPOLLOUT|EPOLLET;
			ev.data.ptr=si;
			si->fd=fd;
			si->callback=send_cb;
			epoll_ctl(eventloop->epfd,EPOLL_CTL_MOD,fd,&ev);

		}
		return n;
}


int send_cb(int fd,int events,void*args)
{
	struct sockitem *si = (struct sockitem*)args;
	send(fd,"hello\n",6,0);
	struct epoll_event ev;
	ev.events=EPOLLIN|EPOLLET;
	ev.data.ptr=si;
	si->fd=fd;
	si->callback=recv_cb;
	epoll_ctl(eventloop->epfd,EPOLL_CTL_MOD,fd,&ev);

}
int main(int argc,char* argv[])
{
	if(argc<2)
	{
		printf("缺少第二个参数(端口信息)");
		return -1;
	}
	
	int port=atoi(argv[1]);

	int sockfd,clientfd;
	sockfd=socket(AF_INET,SOCK_STREAM,0);
	if(sockfd<0)
	{
		printf("创建socket失败");
		return -2;
	}	

	struct sockaddr_in serv_addr;

	memset(&serv_addr,0,sizeof(serv_addr));
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_port=htons(port);
	serv_addr.sin_addr.s_addr=INADDR_ANY;

	if(bind(sockfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr))<0)
	{
		printf("绑定端口失败!");
		return -3;
	}

	if(listen(sockfd,10)<0)
	{
		printf("监听端口失败！");
		return -4;
	}

	int nready;

	eventloop=(struct reactor*)malloc(sizeof(struct reactor));
	eventloop->epfd=epoll_create(256);
	
	if(eventloop->-epfd<0)
	{
		printf("创建epoll失败");
		return -1;
	}	

	struct epoll_event ev,events[1024]={0};
	ev.events=EPOLLIN;
	struct sockitem*si=(struct sockitem*)malloc(sizeof(struct sockitem));
	si->fd=sockfd;
	si->callback=accept_cb;
	
	ev.data.ptr=si;

	epoll_ctl(eventloop->epfd,EPOLL_CTL_ADD,sockfd,&ev);

	
	while(1)
{
	nready=epoll_wait(eventloop->epfd, eventloop->events,1024,-1);
	if(nready<0)
	{
		break;
	}

	int i;
	for(i=0;i<nready;++i)
	{

		if(eventloop->events[i].events & EPOLLIN)
		{
			struct sockitem* si=(struct sockitem*)(eventloop->events[i].data.ptr);
			si->callback(si->fd,eventloop->events[i].events,si);
		}
		if(eventloop->events[i].events& EPOLLOUT)
		{
			struct sockitem* si=(struct sockitem*)(eventloop->events[i].data.ptr);
			si->callback(si->fd,eventloop->events[i].events,si);
		}

	}


}

	return 0;
}